package com.stx.jeepay.merchant.service;

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.stx.jeepay.core.cache.Cache;
import com.stx.jeepay.core.cache.ITokenService;
import com.stx.jeepay.core.cache.RedisUtil;
import com.stx.jeepay.core.dao.SysEntitlementDao;
import com.stx.jeepay.core.entity.SysUserEntity;
import com.stx.jeepay.core.enums.CS;
import com.stx.jeepay.core.expection.BizException;
import com.stx.jeepay.core.expection.JeepayAuthenticationException;
import com.stx.jeepay.core.jwt.JWTPayload;
import com.stx.jeepay.core.jwt.JWTUtils;
import com.stx.jeepay.core.model.security.JeeUserDetails;
import com.stx.jeepay.core.service.SysRoleEntRelaService;
import com.stx.jeepay.core.service.SysRoleService;
import com.stx.jeepay.core.service.SysUserService;
import com.stx.jeepay.merchant.config.SystemYmlConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

@Slf4j
@Service
public class AuthService {

    @Autowired
    private ITokenService tokenService;

    @Resource
    private AuthenticationManager authenticationManager;

    @Autowired
    private SysEntitlementDao sysEntitlementDao;

    @Autowired
    private SysRoleEntRelaService sysRoleEntRelaService;

    @Autowired
    private SysRoleService sysRoleService;

    @Autowired
    private SystemYmlConfig systemYmlConfig;

    @Autowired
    private Cache cache;

    @Autowired
    private SysUserService sysUserService;

    // 验证权限生成Token
    public String auth(String username, String password) {
        //1. 生成spring-security usernamePassword类型对象
        UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(username, password);

        //spring-security 自动认证过程；
        // 1. 进入 JeeUserDetailsServiceImpl.loadUserByUsername 获取用户基本信息；
        //2. SS根据UserDetails接口验证是否用户可用；
        //3. 最后返回loadUserByUsername 封装的对象信息；
        Authentication authentication = null;

        try {
            authentication = authenticationManager.authenticate(upToken);
        } catch (JeepayAuthenticationException jex) {
            throw jex.getBizException() == null ? new BizException(jex.getMessage()) : jex.getBizException();
        } catch (BadCredentialsException e) {
            throw new BizException("用户名/密码错误！");
        } catch (AuthenticationException e) {
            log.error("AuthenticationException:", e);
            throw new BizException("认证服务出现异常， 请重试或联系系统管理员！");
        }

        JeeUserDetails jeeUserDetails = (JeeUserDetails) authentication.getPrincipal();

        //验证通过后 再查询用户角色和权限信息集合
        SysUserEntity sysUser = jeeUserDetails.getSysUser();

        //非超级管理员 && 不包含左侧菜单 进行错误提示
        if (sysUser.getIsAdmin() != CS.YES && sysEntitlementDao.userHasLeftMenu(sysUser.getSysUserId(), CS.SYS_TYPE.MCH) <= 0) {
            throw new BizException("当前用户未分配任何菜单权限，请联系管理员进行分配后再登录！");
        }

        // 放置权限集合
        jeeUserDetails.setAuthorities(getUserAuthority(sysUser));

        //生成token
        String cacheKey = CS.getCacheKeyToken(sysUser.getSysUserId(), IdUtil.fastUUID());

        //生成iToken 并放置到缓存
        tokenService.processTokenCache(jeeUserDetails, cacheKey);

        // 将信息放置到Spring-security context中
        UsernamePasswordAuthenticationToken authenticationRest = new UsernamePasswordAuthenticationToken(jeeUserDetails, null, jeeUserDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationRest);
        //返回JWTToken
        return JWTUtils.generateToken(new JWTPayload(jeeUserDetails), systemYmlConfig.getJwtSecret());
    }

    // 获取用户的权限数据
    private Collection<SimpleGrantedAuthority> getUserAuthority(SysUserEntity sysUser) {
        //用户拥有的角色集合  需要以ROLE_ 开头,  用户拥有的权限集合
        List<String> roleList = sysRoleService.findListByUser(sysUser.getSysUserId());
        List<String> entList = sysRoleEntRelaService.selectEntIdsByUserId(sysUser.getSysUserId(), sysUser.getIsAdmin(), sysUser.getSysType());

        List<SimpleGrantedAuthority> grantedAuthorities = new LinkedList<>();
        roleList.stream().forEach(role -> grantedAuthorities.add(new SimpleGrantedAuthority(role)));
        entList.stream().forEach(ent -> grantedAuthorities.add(new SimpleGrantedAuthority(ent)));
        return grantedAuthorities;
    }

    // 根据用户ID 删除用户缓存信息
    public void delAuthentication(List<Long> sysUserIdList) {
        if (sysUserIdList == null || sysUserIdList.isEmpty()) {
            return;
        }
        for (Long sysUserId : sysUserIdList) {
            Collection<String> cacheKeyList = cache.keys(CS.getCacheKeyToken(sysUserId, "*"));
            if (cacheKeyList == null || cacheKeyList.isEmpty()) {
                continue;
            }
            for (String cacheKey : cacheKeyList) {
                cache.del(cacheKey);
            }
        }
    }

    /**
     * 根据用户ID 更新缓存中的权限集合， 使得分配实时生效
     **/
    public void refAuthentication(List<Long> sysUserIdList) {
        if (sysUserIdList == null || sysUserIdList.isEmpty()) {
            return;
        }
        Map<Long, SysUserEntity> sysUserMap = new HashMap<>();
        sysUserService.list(new LambdaQueryWrapper<SysUserEntity>()
                        .select(SysUserEntity::getSysUserId, SysUserEntity::getState)
                        .in(SysUserEntity::getSysUserId, sysUserIdList))
                .stream().forEach(item -> sysUserMap.put(item.getSysUserId(), item));

        for (Long sysUserId : sysUserIdList) {
            Collection<String> cacheKeyList = cache.keys(CS.getCacheKeyToken(sysUserId, "*"));
            if(cacheKeyList == null || cacheKeyList.isEmpty()){
                continue;
            }
            for (String cacheKey : cacheKeyList) {
                //用户不存在 || 已禁用 需要删除Redis
                if(sysUserMap.get(sysUserId) == null || sysUserMap.get(sysUserId).getState() == CS.PUB_DISABLE){
                    cache.del(cacheKey);
                    continue;
                }
                JeeUserDetails jwtBaseUser = (JeeUserDetails) cache.getObject(cacheKey, JeeUserDetails.class);
                if(jwtBaseUser == null){
                    continue;
                }
                // 重新放置sysUser对象
                jwtBaseUser.setSysUser(sysUserService.getById(sysUserId));

                //查询放置权限数据
                jwtBaseUser.setAuthorities(getUserAuthority(jwtBaseUser.getSysUser()));

                //保存token  失效时间不变
                cache.set(cacheKey, jwtBaseUser);
            }
        }
    }
}
